/* *****************************************************************************
 * JFab (http://code.google.com/p/jfab)
 * Copyright (c) 2011 JFab.org
 * Admin jfab@jeffreythompson.net
 *
 * See the file "LICENSE.txt" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 * *****************************************************************************/

package org.jfab.core.io;

import java.io.StringWriter;
import java.io.Writer;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.jfab.core.collection.CGAnnotationList;
import org.jfab.core.collection.CGConstructorList;
import org.jfab.core.collection.CGFieldList;
import org.jfab.core.collection.CGInitializationBlockList;
import org.jfab.core.collection.CGMethodList;
import org.jfab.core.collection.CGTypeList;
import org.jfab.core.collection.CommentList;
import org.jfab.core.collection.ModifierList;
import org.jfab.core.factory.StringFactory;
import org.jfab.core.factory.TypeReferenceSupport;
import org.jfab.core.formatter.Formatter;
import org.jfab.core.model.CGClass;
import org.jfab.core.model.CGConstructor;
import org.jfab.core.model.CGField;
import org.jfab.core.model.CGMethod;
import org.jfab.core.model.CGType;

/**
 * Provides a class writer.
 */
public final class CGClassWriter extends CGConcreteUnitWriter<CGClass>
{
    /**
     * Construct this object with the given parameters.
     * 
     * @param typeReferenceSupport Type reference support.
     * @param stringFactory String factory.
     * @param basicCodeGenerator Basic code generator.
     * @param fieldWriter Field writer.
     * @param formatter Code formatter.
     * 
     * @param initializationBlockWriter Initialization block writer.
     * @param constructorWriter Constructor writer.
     * @param methodWriter Method writer.
     */
    public CGClassWriter(final TypeReferenceSupport typeReferenceSupport,
            final StringFactory stringFactory,
            final BasicCodeGenerator basicCodeGenerator,
            final CGWriter<CGField> fieldWriter, final Formatter formatter,
            final CGInitializationBlockWriter initializationBlockWriter,
            final CGWriter<CGConstructor> constructorWriter,
            final CGWriter<CGMethod> methodWriter)
    {
        super(typeReferenceSupport, stringFactory, basicCodeGenerator,
                fieldWriter, formatter, initializationBlockWriter,
                constructorWriter, methodWriter);
    }

    /**
     * Write a class.
     * 
     * @param writer Writer.
     * @param cgClass Class.
     */
    @Override
    public final void write(final Writer writer, final CGClass cgClass)
    {
        Validate.notNull(cgClass, "cgClass is null");

        final TypeReferenceSupport typeReferenceSupport = getTypeReferenceSupport();
        typeReferenceSupport.clear();

        if (StringUtils.isNotEmpty(cgClass.getPackageName()))
        {
            typeReferenceSupport.setPackageName(cgClass.getPackageName());
        }

        typeReferenceSupport.determineReferencedTypes(cgClass);

        final String packageName = cgClass.getPackageName();
        final CommentList commentLines = new CommentList(
                cgClass.getCommentLines());
        final CGAnnotationList annotations = new CGAnnotationList(
                cgClass.getAnnotations());
        final ModifierList modifiers = new ModifierList(cgClass.getModifiers());
        final String className = cgClass.getName();
        final List<String> typeParameters = cgClass.getTypeParameters();
        final CGType extendsType = cgClass.getExtendsType();
        final CGTypeList implementsInterfaces = new CGTypeList(
                cgClass.getImplementsInterfaces());
        final Set<String> importClasses = typeReferenceSupport
                .getImportTypeNames(cgClass);

        final StringWriter stringWriter = new StringWriter();

        writePackageDeclaration(stringWriter, packageName);
        getBasicCodeGenerator().writeBlankLine(stringWriter);
        writeImports(stringWriter, importClasses);
        getBasicCodeGenerator().writeBlankLine(stringWriter);
        getBasicCodeGenerator().writeMultilineJavadocComment(stringWriter,
                commentLines);
        writeClassDeclaration(stringWriter, annotations, modifiers, className,
                typeParameters, extendsType, implementsInterfaces);
        getBasicCodeGenerator().writeOpenBrace(stringWriter);

        writeFields(stringWriter, new CGFieldList(cgClass.getFields()));
        writeInitializationBlocks(stringWriter, new CGInitializationBlockList(
                cgClass.getInitializationBlocks()));
        writeConstructors(stringWriter,
                new CGConstructorList(cgClass.getConstructors()));
        writeMethods(stringWriter, new CGMethodList(cgClass.getMethods()));

        getBasicCodeGenerator().writeCloseBrace(stringWriter);

        format(writer, stringWriter);
    }

    /**
     * @param writer Writer.
     * @param annotations Annotations.
     * @param modifiers Modifiers (@see java.lang.reflect.Modifier)
     * @param className Class name.
     * @param typeParameters Type parameters.
     * @param superClass Super class.
     * @param implementsInterfaces Implements interfaces.
     */
    private void writeClassDeclaration(final Writer writer,
            final CGAnnotationList annotations, final ModifierList modifiers,
            final String className, final List<String> typeParameters,
            final CGType superClass, final CGTypeList implementsInterfaces)
    {
        Validate.notNull(writer, "writer is null");
        Validate.notEmpty(className, "className is empty");

        final String annotationsString = getStringFactory()
                .createAnnotationsString(annotations);
        final String modifiersString = getStringFactory()
                .createModifiersString(modifiers);
        final String typeParametersString = getStringFactory()
                .createTypeParametersString(typeParameters);
        final String extendsString = getStringFactory().createExtendsString(
                superClass);
        final String implementsString = getStringFactory()
                .createImplementsString(implementsInterfaces);

        getBasicCodeGenerator().writeLine(
                writer,
                annotationsString + modifiersString + "class " + className
                        + typeParametersString + extendsString
                        + implementsString);
    }
}
